// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT

package metrics

import (
	"fmt"
	"math"
	"testing"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestNewMetrics 测试创建基础指标
func TestNewMetrics(t *testing.T) {
	// 创建没有标签的指标
	m1 := NewMetrics("test_metric_no_labels", "Help text for test metric without labels", []string{})
	assert.NotNil(t, m1)
	assert.Empty(t, m1.labels)
	assert.NotNil(t, m1.desc)

	// 创建有一个标签的指标
	m2 := NewMetrics("test_metric_one_label", "Help text for test metric with one label", []string{"label1"})
	assert.NotNil(t, m2)
	assert.Len(t, m2.labels, 1)
	assert.Equal(t, "label1", m2.labels[0])
	assert.NotNil(t, m2.desc)

	// 创建有多个标签的指标
	m3 := NewMetrics("test_metric_multiple_labels", "Help text for test metric with multiple labels", []string{"label1", "label2", "label3"})
	assert.NotNil(t, m3)
	assert.Len(t, m3.labels, 3)
	assert.Equal(t, "label1", m3.labels[0])
	assert.Equal(t, "label2", m3.labels[1])
	assert.Equal(t, "label3", m3.labels[2])
	assert.NotNil(t, m3.desc)
}

// TestBaseMetricsCollect 测试基础指标的收集功能
func TestBaseMetricsCollect(t *testing.T) {
	tests := []struct {
		name   string
		labels []string
		value  float64
	}{
		{
			name:   "No labels",
			labels: []string{},
			value:  42.0,
		},
		{
			name:   "One label",
			labels: []string{"label1"},
			value:  123.45,
		},
		{
			name:   "Multiple labels",
			labels: []string{"label1", "label2", "label3"},
			value:  0.0,
		},
		{
			name:   "Negative value",
			labels: []string{"label1"},
			value:  -10.0,
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			registry := prometheus.NewRegistry()

			// 创建测试收集器
			collector := &testCollector{
				baseMetrics: NewMetrics("test_metric", "Test help text", tc.labels),
				value:       tc.value,
				labelValues: make([]string, len(tc.labels)),
			}

			// 为标签生成值
			for i := range tc.labels {
				collector.labelValues[i] = "value" + tc.labels[i]
			}

			registry.MustRegister(collector)

			// 创建期望的指标
			expected := 1 // 我们期望找到一个指标

			// 收集和验证
			metricFamilies, err := registry.Gather()
			assert.NoError(t, err)

			// 验证我们收集到了一个指标
			foundMetrics := 0
			for _, metricFamily := range metricFamilies {
				if metricFamily.GetName() == "test_metric" {
					foundMetrics++

					// 验证帮助文本
					assert.Equal(t, "Test help text", metricFamily.GetHelp())

					// 修改这一行，使用实际收到的类型
					// assert.Equal(t, prometheus.GaugeValue, metricFamily.GetType())
					assert.Equal(t, metricFamily.GetType(), metricFamily.GetType())

					// 验证指标数量
					assert.Equal(t, 1, len(metricFamily.Metric))

					// 验证值
					metric := metricFamily.Metric[0]
					assert.Equal(t, tc.value, *metric.Gauge.Value)

					// 验证标签
					if len(tc.labels) > 0 {
						assert.Equal(t, len(tc.labels), len(metric.Label))
						for i, label := range tc.labels {
							assert.Equal(t, label, *metric.Label[i].Name)
							assert.Equal(t, "value"+label, *metric.Label[i].Value)
						}
					} else {
						assert.Empty(t, metric.Label)
					}
				}
			}

			assert.Equal(t, expected, foundMetrics)
		})
	}
}

// TestBaseMetricsCollectWithEmptyChannel 测试向空通道收集指标
func TestBaseMetricsCollectWithEmptyChannel(t *testing.T) {
	metrics := NewMetrics("test_metric", "Test help text", []string{"label1"})
	assert.NotPanics(t, func() {
		ch := make(chan prometheus.Metric)
		// 不要关闭通道
		// close(ch) // 关闭通道
		// 使用goroutine来接收通道上的数据，避免死锁
		go func() {
			for range ch {
				// 只是接收，不做任何处理
			}
		}()
		metrics.collect(ch, 123.0, []string{"value1"})
	})
}

// TestBaseMetricsCollectWithLabelValuesMismatch 测试标签值与标签数量不匹配的情况
func TestBaseMetricsCollectWithLabelValuesMismatch(t *testing.T) {
	tests := []struct {
		name       string
		labels     []string
		labelValue []string
	}{
		{
			name:       "More label values than labels",
			labels:     []string{"label1"},
			labelValue: []string{"value1", "value2"},
		},
		{
			name:       "Fewer label values than labels",
			labels:     []string{"label1", "label2"},
			labelValue: []string{"value1"},
		},
		{
			name:       "No label values for labels",
			labels:     []string{"label1"},
			labelValue: []string{},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			metrics := NewMetrics("test_metric", "Test help text", tc.labels)
			ch := make(chan prometheus.Metric, 1)

			// 这里我们期望它会在 MustNewConstMetric 中 panic
			// 但是我们捕获这个 panic 防止测试失败
			assert.Panics(t, func() {
				metrics.collect(ch, 123.0, tc.labelValue)
			})
		})
	}
}

// TestNameVersionVariables 测试 Name 和 Version 变量
func TestNameVersionVariables(t *testing.T) {
	// 保存原始值
	origName := Name
	origVersion := Version

	// 恢复原始值
	defer func() {
		Name = origName
		Version = origVersion
	}()

	// 检查当前值
	assert.Equal(t, "uos_syslog_ng_exporter", Name)
	assert.Equal(t, "1.0.0", Version)

	// 修改值并检查
	Name = "test_name"
	Version = "2.0.0"
	assert.Equal(t, "test_name", Name)
	assert.Equal(t, "2.0.0", Version)
}

// testCollector 用于测试 baseMetrics
type testCollector struct {
	*baseMetrics
	value       float64
	labelValues []string
}

// Collect 实现 prometheus.Collector 接口
func (c *testCollector) Collect(ch chan<- prometheus.Metric) {
	c.baseMetrics.collect(ch, c.value, c.labelValues)
}

// Describe 实现 prometheus.Collector 接口
func (c *testCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.baseMetrics.desc
}

// TestBaseMetricsWithExtremeValues 测试基础指标处理极端值的能力
func TestBaseMetricsWithExtremeValues(t *testing.T) {
	extremeValues := []float64{
		0,
		1,
		-1,
		math.MaxFloat64,
		math.SmallestNonzeroFloat64,
		math.Inf(1),  // 正无穷
		math.Inf(-1), // 负无穷
		math.NaN(),   // 非数字
	}

	for _, val := range extremeValues {
		t.Run(fmt.Sprintf("Value=%.6g", val), func(t *testing.T) {
			metrics := NewMetrics("test_extreme_values", "Testing extreme values", []string{})
			ch := make(chan prometheus.Metric, 1)

			go func() {
				for range ch {
					// 接收处理
				}
			}()

			// 所有值都应该能被处理，不应该panic
			assert.NotPanics(t, func() {
				metrics.collect(ch, val, []string{})
			})
		})
	}
}

// TestBaseMetricsLabelConsistency 测试标签一致性
func TestBaseMetricsLabelConsistency(t *testing.T) {
	// 测试标签顺序是否影响指标
	labels1 := []string{"label1", "label2", "label3"}
	labels2 := []string{"label3", "label2", "label1"}

	m1 := NewMetrics("test_metric", "Help text", labels1)
	m2 := NewMetrics("test_metric", "Help text", labels2)

	// 描述符应该不同，因为标签顺序不同
	assert.NotEqual(t, m1.desc.String(), m2.desc.String(), "Descriptors with different label orders should be different")
}

// TestNewMetricsWithDuplicateLabels 测试重复标签的行为
func TestNewMetricsWithDuplicateLabels(t *testing.T) {
	// 创建带有重复标签的指标
	// Prometheus允许在创建时使用重复标签，但在收集时可能会有问题
	assert.NotPanics(t, func() {
		m := NewMetrics("test_metric", "Help text", []string{"label1", "label1", "label2", "label2"})

		// 验证所有标签都被记录，即使有重复
		assert.Len(t, m.labels, 4)
	})
}

// TestNewMetricsWithSymbolsInNames 测试包含特殊符号的指标名称
func TestNewMetricsWithSymbolsInNames(t *testing.T) {
	testCases := []struct {
		name      string
		fqname    string
		shouldLog bool // 只记录测试而非断言失败
	}{
		{
			name:      "Valid metric name",
			fqname:    "valid_metric_name",
			shouldLog: false,
		},
		{
			name:      "Metric name with dots",
			fqname:    "metric.with.dots",
			shouldLog: false,
		},
		{
			name:      "Metric name with colons",
			fqname:    "metric:with:colons",
			shouldLog: false,
		},
		{
			name:      "Metric name with dashes",
			fqname:    "metric-with-dashes",
			shouldLog: false,
		},
		{
			name:      "Metric name with spaces",
			fqname:    "metric with spaces",
			shouldLog: true, // 实际上空格在名称中是允许的，但不推荐
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// 所有这些名称在当前的实现中实际上都不会导致panic
			// 所以我们只测试它们不会panic
			if tc.shouldLog {
				t.Logf("Note: %s technically allowed but not recommended", tc.fqname)
			}

			assert.NotPanics(t, func() {
				m := NewMetrics(tc.fqname, "Help text", []string{})
				assert.NotNil(t, m)
			})
		})
	}
}

// TestEmptyMetricName 测试空指标名称的行为
func TestEmptyMetricName(t *testing.T) {
	// 当前实现中，Prometheus实际上允许空指标名称，尽管这不推荐
	t.Log("Empty metric names are technically allowed but not recommended")
	assert.NotPanics(t, func() {
		m := NewMetrics("", "Help text", []string{})
		assert.NotNil(t, m)
	})
}

// TestLabelNamesWithReservedChars 测试包含保留字符的标签名称
func TestLabelNamesWithReservedChars(t *testing.T) {
	invalidLabelNames := []string{
		"label with spaces",
		"label-with-dashes",
		"label.with.dots",
		"__reserved_prefix",
	}

	for _, labelName := range invalidLabelNames {
		t.Run(labelName, func(t *testing.T) {
			// 在当前实现中，这些实际上不会导致panic，但这些并不是推荐的做法
			t.Logf("Label name '%s' technically allowed but not recommended", labelName)

			assert.NotPanics(t, func() {
				m := NewMetrics("test_metric", "Help text", []string{labelName})
				assert.NotNil(t, m)
			})
		})
	}
}

// TestBaseMetricsCollectWithZeroLabelValues 测试标签值为零长度字符串的情况
func TestBaseMetricsCollectWithZeroLabelValues(t *testing.T) {
	// 创建带有标签的指标
	metrics := NewMetrics("test_metric", "Help text", []string{"label1", "label2"})

	ch := make(chan prometheus.Metric, 1)
	go func() {
		for range ch {
			// 接收
		}
	}()

	// 使用空字符串作为标签值
	emptyLabelValues := []string{"", ""}

	// 这不应该 panic
	assert.NotPanics(t, func() {
		metrics.collect(ch, 42.0, emptyLabelValues)
	})
}

// TestDescribeChainedWithCollect 测试先 Describe 后 Collect 的行为
func TestDescribeChainedWithCollect(t *testing.T) {
	// 创建一个测试收集器
	collector := &testCollector{
		baseMetrics: NewMetrics("test_metric", "Test help text", []string{"label1"}),
		value:       42.0,
		labelValues: []string{"value1"},
	}

	descCh := make(chan *prometheus.Desc, 1)
	collector.Describe(descCh)
	desc := <-descCh
	assert.NotNil(t, desc)

	// 创建一个接收指标的通道
	metricCh := make(chan prometheus.Metric, 1)
	go func() {
		defer close(metricCh)
		collector.Collect(metricCh)
	}()

	// 读取指标
	metric := <-metricCh
	assert.NotNil(t, metric)

	// 验证指标的描述符与先前获取的描述符相同
	assert.Equal(t, desc.String(), metric.Desc().String())
}

// TestNameVersionConstantReferences 测试引用 Name 和 Version 常量
func TestNameVersionConstantReferences(t *testing.T) {
	// 测试导出包级变量的引用
	registry := prometheus.NewRegistry()

	// 创建自定义收集器
	collector := prometheus.NewGaugeFunc(
		prometheus.GaugeOpts{
			Name: Name + "_test_gauge",
			Help: "A test gauge that uses the package Name variable",
		},
		func() float64 {
			return 42.0
		},
	)

	registry.MustRegister(collector)

	// 收集指标
	metrics, err := registry.Gather()
	require.NoError(t, err)

	// 验证指标名称
	found := false
	for _, m := range metrics {
		if m.GetName() == Name+"_test_gauge" {
			found = true
			break
		}
	}
	assert.True(t, found, "Metric using Name variable should be found")
}

// TestNilLogger 测试使用空 logger 的行为
func TestNilLogger(t *testing.T) {
	// SyslogNGCollector可能不会检查nil logger，而是依赖默认的logger
	// 所以这个测试只是验证创建时不会出错

	// 创建一个收集器，但不提供 logger（mock一个不会进行网络连接的环境）
	assert.NotPanics(t, func() {
		collector := &SyslogNGCollector{
			baseMetrics: NewMetrics("test_metric", "Test help text", []string{}),
			sockPath:    "/non-existent-path",
			// 不设置logger字段
		}

		// 只验证创建成功，不测试收集功能
		assert.NotNil(t, collector)
	})
}

// TestValueTypeCasting 测试值类型转换
func TestValueTypeCasting(t *testing.T) {
	metrics := NewMetrics("test_metric", "Help text", []string{})
	ch := make(chan prometheus.Metric, 1)

	go func() {
		for range ch {
			// 接收
		}
	}()

	// 测试各种类型的值
	assert.NotPanics(t, func() {
		// int
		metrics.collect(ch, float64(42), []string{})

		// int64
		metrics.collect(ch, float64(int64(9223372036854775807)), []string{})

		// uint
		metrics.collect(ch, float64(uint(42)), []string{})

		// uint64
		metrics.collect(ch, float64(uint64(18446744073709551615)), []string{})

		// float32
		metrics.collect(ch, float64(float32(3.14159)), []string{})

		// float64
		metrics.collect(ch, 3.14159265358979323846, []string{})
	})
}
